#include "Service.h"
#include "ServiceHandler.h"
using namespace std;
enum {
	SERVICE_CMD_STOP,
	SERVICE_CMD_CLIENTCMD
};

struct ServiceClientCommand {
	string cmd;
	void* data;
};

struct ServiceCommand {
	int type;
	void* data;
};

struct TimerInfo {
	Service* service;
	int timerId;
	event* tmr;
};

Service::Service(void)
{
	m_handler = NULL;
	evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, m_sockets);
	//evthread_use_windows_threads();
	
	//m_config = event_config_new();
	//event_config_set_flag(m_config, EVENT_BASE_FLAG_STARTUP_IOCP);
	//::event_config_set_num_cpus_hint(m_config, 1);
	//m_base = event_base_new_with_config(m_config);
	m_base = event_base_new();
	evutil_make_socket_nonblocking(m_sockets[0]);
	m_bevRecv = bufferevent_socket_new(m_base, m_sockets[0], BEV_OPT_DEFER_CALLBACKS);
	bufferevent_enable(m_bevRecv, EV_READ);
	bufferevent_setcb(m_bevRecv, onChannelRecv, NULL, NULL, this);
	InitializeCriticalSection(&m_cri);
	m_running = true;
	
}


DWORD Service::doWork( void* data )
{
	Service* service = (Service*)data;
	event_base_dispatch(service->m_base);

	event_base_free(service->m_base);
	service->m_base = NULL;
	//event_config_free(service->m_config);

	service->m_running = false;
	return 0;
}

bool Service::start() 
{
	m_thread = CreateThread(NULL, 0, doWork, this, 0, 0);
	return true;
}

void Service::onChannelRecv( bufferevent* bev, void* data ) 
{
	Service* service = (Service*)data;

	while(evbuffer_get_length(bev->input) >= sizeof(ServiceCommand)) {
		ServiceCommand cmd;
		bufferevent_read(bev, &cmd, sizeof(ServiceCommand));
		
		if(!service->procCommand(cmd.type, cmd.data)) {
			break;
		}
	}
}

bool Service::procCommand( int type, void* data )
{
	switch(type) {
	case SERVICE_CMD_STOP:	
		{
			evutil_closesocket(m_sockets[0]);
			evutil_closesocket(m_sockets[1]);
			bufferevent_free(m_bevRecv);
			memset(m_sockets, 0, sizeof(m_sockets));
			m_bevRecv = NULL;
		}
		return false;
	case SERVICE_CMD_CLIENTCMD:
		{
			ServiceClientCommand* cmd = (ServiceClientCommand*)data;
			if(m_handler) {
				m_handler->handleCommand(cmd->cmd, cmd->data);
			}
			delete cmd;
		}
		return true;
	default:
		return true;
	}

}

void Service::stop() 
{
	ServiceCommand cmd;
	cmd.type = SERVICE_CMD_STOP;
	cmd.data = NULL;
	EnterCriticalSection(&m_cri);
	send(m_sockets[1], (char*)&cmd, sizeof(ServiceCommand), 0);
	LeaveCriticalSection(&m_cri);

	WaitForSingleObject(m_thread, INFINITE);
	CloseHandle(m_thread);
	m_thread = NULL;	

	DeleteCriticalSection(&m_cri);
	libevent_global_shutdown();
}

event_base* Service::getBase()
{
	return m_base;
}

void Service::setHandler( ServiceHandler* handler )
{
	m_handler = handler;
}

void Service::sendCommand( std::string cmd, void* data )
{
	ServiceClientCommand* clientCmd = new ServiceClientCommand;
	clientCmd->cmd = cmd;
	clientCmd->data = data;
	ServiceCommand serviceCommand;

	serviceCommand.type = SERVICE_CMD_CLIENTCMD;
	serviceCommand.data = clientCmd;
	EnterCriticalSection(&m_cri);
	send(m_sockets[1], (char*)&serviceCommand, sizeof(ServiceCommand), 0);
	LeaveCriticalSection(&m_cri);
}

void Service::addTimer( int timerId, int timeoutMs )
{
	map<int, TimerInfo*>::iterator iter = m_mapTimer.find(timerId);
	TimerInfo* timerInfo = NULL;
	int seconds = timeoutMs / 1000;
	int uSeconds = (timeoutMs - seconds * 1000) * 1000;
	timeval val = {seconds, uSeconds};
	if(iter == m_mapTimer.end()) {
		timerInfo = new TimerInfo;
		timerInfo->timerId = timerId;
		timerInfo->tmr = evtimer_new(m_base, &Service::onTimer, timerInfo);
		timerInfo->service = this;
		m_mapTimer[timerId] = timerInfo;
		event_add(timerInfo->tmr, &val);
	} else {
		timerInfo = iter->second;
		evtimer_del(timerInfo->tmr);
		event_add(timerInfo->tmr, &val);
	}
}

void Service::stopTimer( int timerId )
{
	map<int, TimerInfo*>::iterator iter = m_mapTimer.find(timerId);
	if(iter != m_mapTimer.end()) {
		
		TimerInfo* timerInfo = iter->second;
		evtimer_del(timerInfo->tmr);
		event_free(timerInfo->tmr);
		delete timerInfo;
		m_mapTimer.erase(iter);
	}
}

void Service::onTimer( evutil_socket_t fd, short event, void* data )
{
	TimerInfo* timerInfo= (TimerInfo*)data;
	int timerId = timerInfo->timerId;
	if(timerInfo->service->m_handler) {
		timerInfo->service->m_handler->handleServiceTimer(timerInfo->timerId);
	}
	
	if(!timerInfo->service->timerPending(timerId)) {
		timerInfo->service->stopTimer(timerId);
	}

}

bool Service::timerPending( int timerId )
{
	map<int, TimerInfo*>::iterator iter = m_mapTimer.find(timerId);
	if(iter != m_mapTimer.end()) {
		TimerInfo* timerInfo = iter->second;
		if(evtimer_pending(timerInfo->tmr, NULL)) {
			return true;
		}
	}

	return false;
}
